#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         TextBox.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  A NumericBox is a simple Component that permits capturing
#               of numeric imformation during the process of an assessment.
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Component import Component, ComponentInfo
from Products.KnowledgeEngine.Assessible import Assessible
from Products.KnowledgeEngine.AssessmentComponentState import AssessmentComponentState
from Products.KnowledgeEngine.AssessmentComponentValueTypes import *
from Products.KnowledgeEngine.KERLExtensions import KERLExtensions
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.KERFProcessor import *
from Products.KnowledgeEngine.KERLProcessor import InvalidKerlOperator

#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from App.ImageFile import ImageFile

#####################################################################
## Python Library Imports
# (none)

#####################################################################
## Constants
# (none)

#the range restrictions for values
rr_NONE = 0
rr_GT_MINIMUM = 1                       # Value > Min
rr_GTEQ_MINIMUM = 2                     # Value >= Min
rr_LT_MAXIMUM = 3                       # Value < Max
rr_LTEQ_MAXIMUM = 4                     # Value <= Max
rr_GT_MINIMUM_LT_MAXIMUM = 5            # Value > Min and Value < Max
rr_GTEQ_MINIMUM_LT_MAXIMUM = 6          # Value >= Min and Value < Max
rr_GT_MINIMUM_LTEQ_MAXIMUM = 7          # Value > Min and Value <= Max
rr_GTEQ_MINIMUM_LTEQ_MAXIMUM = 8        # Value >= Min and Value <= Max

#the display enumeration values for the range restriction property
rr_DISPLAYVALUES = [("(none)",rr_NONE), ("Values > Minimum",rr_GT_MINIMUM), ("Values >= Minimum",rr_GTEQ_MINIMUM), ("Values < Maximum",rr_LT_MAXIMUM), ("Values <= Maximum",rr_LTEQ_MAXIMUM), ("Values > Minimum and Values < Maximum",rr_GT_MINIMUM_LT_MAXIMUM), ("Values >= Minimum and Values < Maximum",rr_GTEQ_MINIMUM_LT_MAXIMUM), ("Values > Minimum and Values <= Maximum",rr_GT_MINIMUM_LTEQ_MAXIMUM), ("Values >= Minimum and Values <= Maximum",rr_GTEQ_MINIMUM_LTEQ_MAXIMUM)]

#####################################################################
## NumericBoxInfo Class Definition
class NumericBoxInfo(ComponentInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """

    #---------------------------------------------------------------#

    def getClassName(self):
        """
            Return the class name
        """

        return 'NumericBox'

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """

        return "Numeric Question"

    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.
        """

        return "num"

    #---------------------------------------------------------------#

    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """

        return ImageFile('skins/NumericBox_icon.gif', globals())

    #---------------------------------------------------------------#

    def isInstanceOf(self, className):
        """
            Returns if the class is an instance of the specified
            className, or implements the interface specified
            by the className
        """

        return className == 'Assessible' or ComponentInfo.isInstanceOf(self, className)

    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific properties using the PropertyReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class properties for reflection
        ComponentInfo.onConfigureProperties(self)

        #add the 'reportContent' property
        self.addPropertyDefinition(PropertyDefinition(identity="reportContent", displayName="Report Content", description="Text to be displayed for this component when used in a report", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_HTML, propertyStructure=ps_ATOMIC))

        #add the 'nrDecimalPlaces' property
        self.addPropertyDefinition(PropertyDefinition(identity="nrDecimalPlaces", displayName="Precision", description="The number of decimal places after the decimal place", defaultValue=2, propertyType=pt_INTEGER, propertyFormat=pf_NATIVE, propertyStructure=ps_ATOMIC, hasEnumeratedValues=true, enumeratedValuesList=[0,1,2,3,4,5,6,7,8,9,10]))

        #add the 'defaultValue' property
        self.addPropertyDefinition(PropertyDefinition(identity="defaultValue", displayName="Default Value", description="Initial value for new assessments", defaultValue=0, propertyType=pt_FLOAT, propertyFormat=pf_NATIVE, propertyStructure=ps_ATOMIC))

        #add the 'minimumValue' property
        self.addPropertyDefinition(PropertyDefinition(identity="minimumValue", displayName="Minimum Value", description="The minimum value (when range restriction is used)", defaultValue=0, propertyType=pt_FLOAT, propertyFormat=pf_NATIVE, propertyStructure=ps_ATOMIC))

        #add the 'maximumValue' property
        self.addPropertyDefinition(PropertyDefinition(identity="maximumValue", displayName="Maximum Value", description="The maximum value (when range restriction is used)", defaultValue=0, propertyType=pt_FLOAT, propertyFormat=pf_NATIVE, propertyStructure=ps_ATOMIC))

        #add the 'rangeRestriction' property
        self.addPropertyDefinition(PropertyDefinition(identity="rangeRestriction", displayName="Range Restriction", description="Criteria used to restrict the range of values during an assessment", defaultValue=rr_NONE, propertyType=pt_INTEGER, propertyFormat=pf_NATIVE, propertyStructure=ps_ATOMIC, hasEnumeratedValues=true, enumeratedValuesMapping=rr_DISPLAYVALUES))

    #---------------------------------------------------------------#

#####################################################################
## NumericBox Class Definition
class NumericBox(Component, Assessible, KERLExtensions):
    """
        Defines a simple component to represent one or more lines of
        text entered during an assessment.
    """

    #---------------------------------------------------------------#

    #TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = NumericBoxInfo()

    #---------------------------------------------------------------#

    # DTML Methods
    index_html = DTMLFile('dtml/index_html', globals())
    simplereport_html = DTMLFile('dtml/simplereport_html', globals())

    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the NumericBox"

        Component.__init__(self, id, identity)

    #---------------------------------------------------------------#

    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the
            identity of this object has changed.

            The identityMappings dictionary holds
            mappings from oldIdentities -> newIdentities
            (including this object)

        """

        #perform default identity changes
        Component.onAfterIdentityChange(self, identityMappings)

    #---------------------------------------------------------------#

    def getDefaultValue(self):
        "Returns the component assessment default value"
        return self.defaultValue

    #---------------------------------------------------------------#

    def setDefaultValue(self, defaultValue):
        "Set the component assessment default value"
        self.defaultValue = defaultValue

    #---------------------------------------------------------------#

    def getRangeRestriction(self):
        """
            Returns the type of range restiction for assessment values
            see: rr constants at the top of this file
        """

        return self.rangeRestriction

    #---------------------------------------------------------------#

    def getMinimumValue(self):
        """
            Returns the minimum for assessment values
        """

        return self.minimumValue

    #---------------------------------------------------------------#

    def getMaximumValue(self):
        """
            Returns the maximum for assessment values
        """

        return self.maximumValue

    #---------------------------------------------------------------#

    def getNrDecimalPlaces(self):
        """
            Returns the number of desired decimal places
        """

        return int(self.nrDecimalPlaces)

    #---------------------------------------------------------------#

    def asString(self, value):
        """
            Formats the specified numeric value according to the
            formatting properties of this component
        """

        #create the appropriate format string based on the properties of this component
        if self.getNrDecimalPlaces() > 0:
            #has decimal places
            return ("%5.*f" % (self.getNrDecimalPlaces(), float(value))).strip()

        else:
            #no decimal places
            return ("%d" % int(round(float(value)))).strip()

    #---------------------------------------------------------------#

    def getComponentProperty(self, identity, propertyName, assessment = None):
        """
            Returns the value of the specified property, given the
            identity of the component (which may be used to lookup states) and
            an assessment.

            RETURNS: None if the property doesn't exist
        """

        if propertyName == "value":
            return self.asString(assessment.getComponentState(identity).getValue())
        else:
            return Component.getComponentProperty(self, identity, propertyName, assessment)

    #---------------------------------------------------------------#

    def addJavaScriptOnLoadStatements(self, onLoadStatementsDictionary):
        """
            ABSTRACT: Adds the necessary onLoad statements to the
            onLoadStatementsDictionary which is defined as {KEY -> string}.

            The KEY is used to determine which component generated
            the statements.  The KEY may be used to determine if
            statements have already been generated for this type
            of component.

            The STRING is the actual javascript statements.

            NOTE: When overriding, this method should always be called
            from sub-classes.
        """

        #perform the super-class additions
        Component.addJavaScriptOnLoadStatements(self, onLoadStatementsDictionary)

    #---------------------------------------------------------------#

    def addJavaScriptDeclarations(self, declarationsDictionary):
        """
            ABSTRACT: Adds the necessary javascript declarations to
            the declarationsDictionary which is defined as {KEY -> string}.

            The KEY is used to determine which component generated
            the declarations.  The KEY may be used to determine if
            declarations have already been generated for this type
            of component.

            The STRING is the actual javascript declaration(s).

            NOTE: When overriding, this method should always be called
            from sub-classes.
        """

        #perform the super-class additions
        Component.addJavaScriptDeclarations(self, declarationsDictionary)

        #add javascript declarations only if they don't already exist for this component class
        declarationsDictionary[self.getClassInfo().getClassName()] = JAVASCRIPT_DECLARATIONS


    #---------------------------------------------------------------#
    # BEGIN: Assessible Implementation                              #
    #---------------------------------------------------------------#

    def getAssessmentValueType(self):
        """
            ABSTRACT: Returns the type of the value that is to be stored
            for this component when it's assessment state is persisted.

            NOTE 1: See AssessmentComponentValueTypes for a list of defined contants
            NOTE 2: This method MUST be overriden in the implementing
            class to specify the correct type for value persistence

            RETURN TYPE: String
        """

        #numericbox assessment values are floating point
        return st_FLOAT

    #---------------------------------------------------------------#

    def getDefaultAssessmentState(self, assessment):
        """
            ABSTRACT: Constructs and returns a suitable 'default'
            AssessmentComponentState for the provided Assessment
            instance.

            NOTE: This method MUST be overriden in the implementing
            class to provide a suitable default value for the component

            RETURN TYPE: AssessmentComponentState
        """

        return AssessmentComponentState(assessment.getIdentity(), assessment.getModelIdentity(), self.getIdentity(), self.getAssessmentValueType(), self.getDefaultValue(), revision=assessment.getRevision())

    #---------------------------------------------------------------#

    def updateAssessmentState(self, assessment, formValue):
        """
            ABSTRACT: Updates the specified assessment using
            the provided HTTP REQUEST formValue.

            NOTE: This method MUST be overriden in the implementing
            class to suitably update the component state
        """

        Component.updateAssessmentState( self, assessment, formValue )

        #get the current assessment state for the component from the assessment
        state = assessment.getComponentState(self.getIdentity())

        #update the current state
        try:
            floatValue = float(formValue)
        except ValueError:
            floatValue = 0
        
        state.setValue(floatValue)

        searchIndex = getattr(self, 'searchIndex', False)
        if searchIndex:
            state.setSearchIndex(searchIndex)
            state.setSearchValue(str(formValue))
            
            
        #update the assessment
        assessment.setIsModified(true)

    #---------------------------------------------------------------#
    # END: Assessible Implementation                                #
    #---------------------------------------------------------------#

    #---------------------------------------------------------------#
    # BEGIN: KERLExtensions Implementation                          #
    #---------------------------------------------------------------#

    def compileKERLtoJavaScript(self, expression):
        """
            Returns a Python string that represents a JavaScript
            expression, which may be used to evaluate the status
            of the component class specific KERL expression.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.

            RETURN TYPE: String
        """

        #example: cond="['num1','equals', value]" returns 'asNumber(document.getElementById("num1").value) == value'
        #example: cond="['num1','doesntequal', value]" returns 'asNumber(document.getElementById("num1").value) != value'
        #example: cond="['num1','lessthan', value]" returns 'asNumber(document.getElementById("num1").value) < value'
        #example: cond="['num1','greaterthan', value]" returns 'asNumber(document.getElementById("num1").value) > value'
        #example: cond="['num1','lessthanequal', value]" returns 'asNumber(document.getElementById("num1").value) <= value'
        #example: cond="['num1','greaterthanequal', value]" returns 'asNumber(document.getElementById("num1").value) >= value'
        #example: cond="['num1','inrange', value1, value2]" returns 'asNumber(document.getElementById("num1").value) >= value1 && asNumber(document.getElementById("num1").value) <= value2'

        #get the identity (which is the identity of itself)
        identity = self.getIdentity()

        #get the operator
        opr = expression[1]

        #get the value
        value = expression[2]

        #determine the result
        result = "asNumber(document.getElementById(\"" + identity + "\").value)"

        if opr == "equals" or opr == "equal" or opr == "==" or opr == "=":
            return result + "==" + self.asString(value)

        elif opr == "doesntequal" or opr == "doesntequals" or opr == "notequal" or opr == "notequals" or opr == "!=" or opr == "<>":
            return result + "!=" + self.asString(value)

        elif opr == "lessthan" or opr == "<":
            return result + "<" + self.asString(value)

        elif opr == "greaterthan" or opr == ">":
            return result + ">" + self.asString(value)

        elif opr == "lessthanequal" or opr == "lessthanequals" or opr == "<=":
            return result + "<=" + self.asString(value)

        elif opr == "greaterthanequal" or opr == "greaterthanequals" or opr == ">=":
            return result + ">=" + self.asString(value)

        elif opr == "inrange":
            return result + ">=" + self.asString(expression[2]) + " && " + result + "<=" + self.asString(expression[3])

        else:
            raise InvalidKerlOperator, opr
            #return "true"

    #---------------------------------------------------------------#

    def compileKERLtoEnglish(self, expression):
        """
            Returns a string representing an 'English' version
            of the component class specific KERL expression.

            NOTE: this method is primarily used to produce
            English descriptions of KERL expressions that may be 
            used to describe things like AssessmentFilters
            
            RETURN TYPE: String         
        """
        
        #get the operator and value
        operator = expression[1]
        value = expression[2]

        #determine the result
        result = "%s '%s' " % (self.getClassInfo().getDisplayName(), self.getTitleOrId())

        if opr == "equals" or opr == "equal" or opr == "==" or opr == "=":
            return result + " equal to " + value

        elif opr == "doesntequal" or opr == "doesntequals" or opr == "notequal" or opr == "notequals" or opr == "!=" or opr == "<>":
            return result + " not equal to " + value

        elif opr == "lessthan" or opr == "<":
            return result + " less than " + value

        elif opr == "greaterthan" or opr == ">":
            return result + " greater than " + value

        elif opr == "lessthanequal" or opr == "lessthanequals" or opr == "<=":
            return result + " less than or equals to " + value

        elif opr == "greaterthanequal" or opr == "greaterthanequals" or opr == ">=":
            return result + " greater than or equals to " + value

        elif opr == "inrange":
            return result + " in range " + expression[2] + " to " + expression[3] + " (inclusive)"

        else:
            raise InvalidKerlOperator, opr
            #return ""

    #---------------------------------------------------------------#

    def evaluate(self, expression, assessment):
        """
            Evalutes the component class specific KERL expression
            using the provided assessment.

            NOTE: this method is primarily used to produce
            JavaScript expressions that form the basis of the
            client-side showif technology.

            RETURN TYPE: Boolean
        """

        #get the identity
        identity = self.getIdentity()

        #get the operator
        opr = expression[1]

        #get the value
        value = expression[2]

        #get the stored state
        state = assessment.getComponentState(identity)

        #evaluate the expression
        if opr == "equals" or opr == "equal" or opr == "==" or opr == "=":
            return state.getValue() == value

        elif opr == "doesntequal" or opr == "doesntequals" or opr == "notequal" or opr == "notequals" or opr == "!=" or opr == "<>":
            return state.getValue() != value

        elif opr == "lessthan" or opr == "<":
            return state.getValue() < value

        elif opr == "greaterthan" or opr == ">":
            return state.getValue() > value

        elif opr == "lessthanequal" or opr == "lessthanequals" or opr == "<=":
            return state.getValue() <= value

        elif opr == "greaterthanequal" or opr == "greaterthanequals" or opr == ">=":
            return state.getValue() >= value

        elif opr == "inrange":
            return state.getValue() >= expression[2] and state.getValue() <= expression[3]

        else:
            raise InvalidKerlOperator, opr
            #return true

    #---------------------------------------------------------------#

    def getDependencies(self, expression):
        """
            Returns a list of components that are required to
            evaluate the component class specific KERL expression.

            NOTE: Includes the specific component itself.

            RETURN TYPE: {Component} indexed by Component.getIdentity()
       """

        return {self.getIdentity():self}

    #---------------------------------------------------------------#

    def updateKERLIdentities(self, expression, identityMappings):
        """
            Returns the expression with the appropriate old identities
            (as identified by the identityMappings) replaced with
            new identities.
        """

        #get the component identity from the expression (first element)
        identity = expression[0]

        #is the component in the identityMappings?
        if identityMappings.has_key(identity):
            expression[0] = identityMappings[identity]

        return expression

    #---------------------------------------------------------------#
    # END: KERLExtensions Implementation                            #
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation

# Register Component Information with the Knowledge Engine
registerGenericObjectClass(NumericBox)

#####################################################################

JAVASCRIPT_DECLARATIONS = """
//------------------------------------------------------------------------------
//  Multi-Locale Numeric Management Routines
//------------------------------------------------------------------------------

//The standard US locale number separators (redefine these to support a different locale)
var DECIMAL_SEPARATOR = ".";
var THOUSANDS_SEPARATOR = ",";

//------------------------------------------------------------------------------


    function isNumber(strValue) {
    //purpose:  determine if the specified strValue string is a valid number using
    //          the globally defined decmial and thousands separators

        var NUMBER_RE = new RegExp("^\\\\s*[\\\\-\\\\+]?\\\\d{1,3}(\\\\" + THOUSANDS_SEPARATOR + "?\\\\d{3})*(\\\\" + DECIMAL_SEPARATOR + "\\\\d+)?\\\\s*$");

        //does the number start with a leading decimal?
        if (strValue.length > 0 && strValue.charAt(0) == DECIMAL_SEPARATOR)
            strValue = '0' + strValue;

        return NUMBER_RE.test(strValue);
    } //isNumber

//------------------------------------------------------------------------------

    function asNumber(strValue) {
    //purpose:  attempt to convert the strValue string containing a locale specific
    //          formatted number into a number value.
    //returns:  NaN if not a number

        //does the number start with a leading decimal?
        if (strValue.length > 0 && strValue.charAt(0) == DECIMAL_SEPARATOR)
            strValue = '0' + strValue;

        //ensure the string is a number
        if (isNumber(strValue)) {
            strValue = strValue.replace(new RegExp("\\\\" + THOUSANDS_SEPARATOR, "g"), "");
            strValue = strValue.replace("+", "");
            strValue = strValue.replace(/\\s/g, "");
            strValue = strValue.replace(DECIMAL_SEPARATOR, ".");
            return new Number(strValue);
        } else {
            return NaN;
        } //if
    } //asNumber

//------------------------------------------------------------------------------

    function asString(nrValue, nbrDecimalPlaces, boolDontUseThousandsSeparator) {
    //purpose:  converts the specified number into a string containing the
    //          specified number of fractional digits.

        var strValue = toFixed(new Number(nrValue), nbrDecimalPlaces);

        var arrstrNumber = strValue.split(".");
        var strNumbers = arrstrNumber[0];
        if (nrValue < 0) strNumbers = strNumbers.substring(1);

        if (nbrDecimalPlaces > 0) {
            var strDigits = DECIMAL_SEPARATOR + arrstrNumber[1];
        } else {
            var strDigits = "";
        } //if

        var strResult = "";
        if (!boolDontUseThousandsSeparator) {
            for (var i = strNumbers.length; i >= 0; i-=3) {
                strResult = strNumbers.substring(i-3, i) + strResult;
                if (i-3 > 0) strResult = THOUSANDS_SEPARATOR + strResult;
            } //for
        } else {
            strResult = strNumbers;
        } //if
        return (nrValue < 0 ? "-" : "") + strResult + strDigits;
    } //asString

//------------------------------------------------------------------------------

    function toFixed(nbrValue, nbrDecimalPlaces) {

        var lnbrResult = Math.round(nbrValue * Math.pow(10, nbrDecimalPlaces)) / Math.pow(10, nbrDecimalPlaces);
        var lstrResult = "" + lnbrResult;

        if (nbrDecimalPlaces > 0 && lstrResult.indexOf(".") < 0)
            lstrResult += ".0";

        for(var i = 0; i < nbrDecimalPlaces; i++)
            lstrResult += "0";

        var lIdx = lstrResult.indexOf(".");
        if (nbrDecimalPlaces == 0) {
            if (lIdx >= 0)
                lstrResult = lstrResult.substr(0, lIdx);
        } else if (lstrResult.length - lIdx != nbrDecimalPlaces) {
            lstrResult = lstrResult.substr(0, lIdx + nbrDecimalPlaces + 1);
        } //if

        return lstrResult;
    } //toFixed

//------------------------------------------------------------------------------

    var lastNumber = null;

    function validateNumber(txtNumber, rangeRestriction, nbrMinimum, nbrMaximum, nbrDecimalPlaces) 
    {

        var warningDiv = document.getElementById( 'warning-' + txtNumber.name );
        
        function warn(message)
        {
            
            if(!warningDiv)
            {
                var parent = txtNumber.parentNode;
                warningDiv = document.createElement('div');
                warningDiv.className = "error keError numericBoxError";
                warningDiv.id="warning-" + txtNumber.name;
                parent.insertBefore( warningDiv, txtNumber );
            }
            
            warningDiv.style.display="block";
            warningDiv.innerHTML = message;
        }


        //get the numer value
        var value = txtNumber.value;
        
        // Don't keep popping up if the number is the same as last time. 
        //if( value == lastNumber ) return;
        
        //lastNumber = value;
        
        var wasError = false;

        //enforce appropriate range restrictions
        if (rangeRestriction == 0) {            //no range restriction
            if (!isNumber(value)) {
                warn("Value must be a number");
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 1) {     //value > minimum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) > nbrMinimum))) {
                warn("Value must be a number greater than " + asString(nbrMinimum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 2) {     //value >= minimum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) >= nbrMinimum))) {
                warn("Value must be a number greater than or equal to " + asString(nbrMinimum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 3) {     //value < maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) < nbrMaximum))) {
                warn("Value must be less than " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 4) {     //value <= maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) <= nbrMaximum))) {
                warn("Value must be less than or equal to " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 5) {     //value > minimum && value < maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) > nbrMinimum && asNumber(value) < nbrMaximum))) {
                warn("Value must be greater than " + asString(nbrMinimum, nbrDecimalPlaces, true) + "\\nand less than " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 6) {     //value >= minimum && value < maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) >= nbrMinimum && asNumber(value) < nbrMaximum))) {
                warn("Value must be greater than or equal to " + asString(nbrMinimum, nbrDecimalPlaces, true) + "\\nand less than " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 7) {     //value > minimum && value <= maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) > nbrMinimum && asNumber(value) <= nbrMaximum))) {
                warn("Value must be greater than " + asString(nbrMinimum, nbrDecimalPlaces, true) + "\\nand less than or equal to " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber

        } else if (rangeRestriction == 8) {     //value >= minimum && value <= maximum
            if (!isNumber(value) || (isNumber(value) && !(asNumber(value) >= nbrMinimum && asNumber(value) <= nbrMaximum))) {
                warn("Value must be greater than or equal to " + asString(nbrMinimum, nbrDecimalPlaces, true) + "\\nand less than or equal to " + asString(nbrMaximum, nbrDecimalPlaces, true));
                wasError = true;
            } //if - !isNumber
        } //if - range restriction test

        //check number range
        if (!wasError && (asNumber(value) < -9999999999999.9999999999 || asNumber(value) > 99999999999999.9999999999)) {
            warn("Value must be not be less than -9999999999999.9999999999\\nor greater than 99999999999999.9999999999");
            wasError = true;
        } //if

        if (wasError) {
            //txtNumber.focus();
        } else {
            if(warningDiv)
            {
                warningDiv.style.display="none";
            }
            txtNumber.value = asString(asNumber(value), nbrDecimalPlaces, true);
        } //if
    } //validateNumber

//------------------------------------------------------------------------------
"""
